using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace PocketLibrary.Media
{
    public class ImageHandling
    {
        public Bitmap ResizeImage(System.Drawing.Image image, int width)
        {
            int height;

            // measure the image and calculate size if it's more than width wide
            System.Drawing.Image baseImage = image;

            if (baseImage.Width > width)
            {
                float actualWidth = baseImage.Width;
                float actualHeight = baseImage.Height;
                float maxWidth = width;
                //width = width;
                height = Convert.ToInt32((maxWidth / actualWidth) * actualHeight);
            }
            else
            {
                width = baseImage.Width;
                height = baseImage.Height;
            }

            Bitmap thumbImage = new Bitmap(width, height);
            Graphics thumbgraphic = Graphics.FromImage(thumbImage);

            thumbgraphic.DrawImage(baseImage,
                new Rectangle(0, 0, width, height), 0, 0, image.Width, image.Height,
                GraphicsUnit.Pixel, new ImageAttributes());

            baseImage.Dispose();
            return thumbImage;
        }

        public Bitmap ResizeImageAlternate(System.Drawing.Image image, int width)
        {
            int height;

            // measure the image and calculate size if it's more than width wide
            System.Drawing.Image baseImage = image;

            if (baseImage.Width > width)
            {
                float actualWidth = baseImage.Width;
                float actualHeight = baseImage.Height;
                float maxWidth = width;
                //width = width;
                height = Convert.ToInt32((maxWidth / actualWidth) * actualHeight);
            }
            else
            {
                width = baseImage.Width;
                height = baseImage.Height;
            }

            Bitmap thumbImage = new Bitmap(width, height);
            Graphics thumbgraphic = Graphics.FromImage(thumbImage);

            //thumbgraphic.DrawImage(baseImage, new Rectangle(0, 0, width, height), 0, 0, image.Width, image.Height,
            //    GraphicsUnit.Pixel, new ImageAttributes());

            thumbgraphic.DrawImage(baseImage, new Rectangle(0, 0, width, height),
                new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);

            baseImage.Dispose();
            return thumbImage;
        }


        public Bitmap Crop(System.Drawing.Image imgPhoto, int Width, int Height)
        {
            return Crop(imgPhoto, Width, Height, AnchorPosition.Center);
        }

        public Bitmap Crop(System.Drawing.Image imgPhoto, int Width, int Height,
            AnchorPosition Anchor)
        {
            int sourceWidth = imgPhoto.Width;
            int sourceHeight = imgPhoto.Height;
            int sourceX = 0;
            int sourceY = 0;
            int destX = 0;
            int destY = 0;

            float nPercent = 0;
            float nPercentW = 0;
            float nPercentH = 0;

            nPercentW = ((float)Width / (float)sourceWidth);
            nPercentH = ((float)Height / (float)sourceHeight);

            if (nPercentH < nPercentW)
            {
                nPercent = nPercentW;
                switch (Anchor)
                {
                    case AnchorPosition.Top:
                        destY = 0;
                        break;
                    case AnchorPosition.Bottom:
                        destY = (int)
                            (Height - (sourceHeight * nPercent));
                        break;
                    default:
                        destY = (int)
                            ((Height - (sourceHeight * nPercent)) / 2);
                        break;
                }
            }
            else
            {
                nPercent = nPercentH;
                switch (Anchor)
                {
                    case AnchorPosition.Left:
                        destX = 0;
                        break;
                    case AnchorPosition.Right:
                        destX = (int)
                          (Width - (sourceWidth * nPercent));
                        break;
                    default:
                        destX = (int)
                          ((Width - (sourceWidth * nPercent)) / 2);
                        break;
                }
            }

            int destWidth = (int)(sourceWidth * nPercent);
            int destHeight = (int)(sourceHeight * nPercent);

            Bitmap bmPhoto = new Bitmap(Width,
                    Height, PixelFormat.Format24bppRgb);
            //bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
            //        imgPhoto.VerticalResolution);


            Graphics grPhoto = Graphics.FromImage(bmPhoto);
            //grPhoto.InterpolationMode =
            //        InterpolationMode.HighQualityBicubic;

            grPhoto.DrawImage(imgPhoto,
                new Rectangle(destX, destY, destWidth, destHeight),
                new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
                GraphicsUnit.Pixel);

            grPhoto.Dispose();
            return bmPhoto;
        }

        public Bitmap Rotate90DegreesCW(System.Drawing.Image img)
        {
            //return RotateImage(img, 90f);
            return Bitmap_Rotate(90, 0, (Bitmap)img);
        }

        public Bitmap Rotate90DegreesCCW(System.Drawing.Image img, int angle)
        {
            return RotateImage(img, -90f);
        }

        /// <summary>
        /// Creates a new Image containing the same image only rotated
        /// </summary>
        /// <param name="image">The <see cref="System.Drawing.Image"/> to rotate</param>
        /// <param name="angle">The amount to rotate the image, clockwise, in degrees</param>
        /// <returns>A new <see cref="System.Drawing.Bitmap"/> that is just large enough
        /// to contain the rotated image without cutting any corners off.</returns>
        /// <exception cref="System.ArgumentNullException">Thrown if <see cref="image"/> is null.</exception>
        public static Bitmap RotateImage(System.Drawing.Image image, float angle)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            const double pi2 = Math.PI / 2.0;

            // Why can't C# allow these to be const, or at least readonly
            // *sigh*  I'm starting to talk like Christian Graus :omg:
            double oldWidth = (double)image.Width;
            double oldHeight = (double)image.Height;

            // Convert degrees to radians
            double theta = ((double)angle) * Math.PI / 180.0;
            double locked_theta = theta;

            // Ensure theta is now [0, 2pi)
            while (locked_theta < 0.0)
                locked_theta += 2 * Math.PI;

            double newWidth, newHeight;
            int nWidth, nHeight; // The newWidth/newHeight expressed as ints

            #region Explaination of the calculations
            /*
			 * The trig involved in calculating the new width and height
			 * is fairly simple; the hard part was remembering that when 
			 * PI/2 <= theta <= PI and 3PI/2 <= theta < 2PI the width and 
			 * height are switched.
			 * 
			 * When you rotate a rectangle, r, the bounding box surrounding r
			 * contains for right-triangles of empty space.  Each of the 
			 * triangles hypotenuse's are a known length, either the width or
			 * the height of r.  Because we know the length of the hypotenuse
			 * and we have a known angle of rotation, we can use the trig
			 * function identities to find the length of the other two sides.
			 * 
			 * sine = opposite/hypotenuse
			 * cosine = adjacent/hypotenuse
			 * 
			 * solving for the unknown we get
			 * 
			 * opposite = sine * hypotenuse
			 * adjacent = cosine * hypotenuse
			 * 
			 * Another interesting point about these triangles is that there
			 * are only two different triangles. The proof for which is easy
			 * to see, but its been too long since I've written a proof that
			 * I can't explain it well enough to want to publish it.  
			 * 
			 * Just trust me when I say the triangles formed by the lengths 
			 * width are always the same (for a given theta) and the same 
			 * goes for the height of r.
			 * 
			 * Rather than associate the opposite/adjacent sides with the
			 * width and height of the original bitmap, I'll associate them
			 * based on their position.
			 * 
			 * adjacent/oppositeTop will refer to the triangles making up the 
			 * upper right and lower left corners
			 * 
			 * adjacent/oppositeBottom will refer to the triangles making up 
			 * the upper left and lower right corners
			 * 
			 * The names are based on the right side corners, because thats 
			 * where I did my work on paper (the right side).
			 * 
			 * Now if you draw this out, you will see that the width of the 
			 * bounding box is calculated by adding together adjacentTop and 
			 * oppositeBottom while the height is calculate by adding 
			 * together adjacentBottom and oppositeTop.
			 */
            #endregion

            double adjacentTop, oppositeTop;
            double adjacentBottom, oppositeBottom;

            // We need to calculate the sides of the triangles based
            // on how much rotation is being done to the bitmap.
            //   Refer to the first paragraph in the explaination above for 
            //   reasons why.
            if ((locked_theta >= 0.0 && locked_theta < pi2) ||
                (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2)))
            {
                adjacentTop = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
                oppositeTop = Math.Abs(Math.Sin(locked_theta)) * oldWidth;

                adjacentBottom = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
                oppositeBottom = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
            }
            else
            {
                adjacentTop = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
                oppositeTop = Math.Abs(Math.Cos(locked_theta)) * oldHeight;

                adjacentBottom = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
                oppositeBottom = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
            }

            newWidth = adjacentTop + oppositeBottom;
            newHeight = adjacentBottom + oppositeTop;

            nWidth = (int)Math.Ceiling(newWidth);
            nHeight = (int)Math.Ceiling(newHeight);

            Bitmap rotatedBmp = new Bitmap(nWidth, nHeight);

            using (Graphics g = Graphics.FromImage(rotatedBmp))
            {
                // This array will be used to pass in the three points that 
                // make up the rotated image
                Point[] points;

                /*
                 * The values of opposite/adjacentTop/Bottom are referring to 
                 * fixed locations instead of in relation to the
                 * rotating image so I need to change which values are used
                 * based on the how much the image is rotating.
                 * 
                 * For each point, one of the coordinates will always be 0, 
                 * nWidth, or nHeight.  This because the Bitmap we are drawing on
                 * is the bounding box for the rotated bitmap.  If both of the 
                 * corrdinates for any of the given points wasn't in the set above
                 * then the bitmap we are drawing on WOULDN'T be the bounding box
                 * as required.
                 */
                if (locked_theta >= 0.0 && locked_theta < pi2)
                {
                    points = new Point[] { 
											 new Point( (int) oppositeBottom, 0 ), 
											 new Point( nWidth, (int) oppositeTop ),
											 new Point( 0, (int) adjacentBottom )
										 };

                }
                else if (locked_theta >= pi2 && locked_theta < Math.PI)
                {
                    points = new Point[] { 
											 new Point( nWidth, (int) oppositeTop ),
											 new Point( (int) adjacentTop, nHeight ),
											 new Point( (int) oppositeBottom, 0 )						 
										 };
                }
                else if (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2))
                {
                    points = new Point[] { 
											 new Point( (int) adjacentTop, nHeight ), 
											 new Point( 0, (int) adjacentBottom ),
											 new Point( nWidth, (int) oppositeTop )
										 };
                }
                else
                {
                    points = new Point[] { 
											 new Point( 0, (int) adjacentBottom ), 
											 new Point( (int) oppositeBottom, 0 ),
											 new Point( (int) adjacentTop, nHeight )		
										 };
                }

                //g.DrawImage(image, points);

            }

            return rotatedBmp;
        }

        #region Bitmap_Rotate
        /// <summary>
        /// <list type="table">
        /// <item><description>Rotate Bitmap</description></item>
        /// </list>
        /// The SetPixel, GetPixel calls are extremly slow on CF.
        /// <remarks>
        /// <para>bmp.Width = 29 ; bmp.Height = 41</para>
        /// <para>0 ; i_Mirror=0 : Left,Top(0,0) will become Left,Top(0,0)</para>
        /// <para>0 ; i_Mirror=1 : Left,Top(0,0) will become Right,Top(29,0)</para>
        /// <para>90 ; i_Mirror=0 : Left,Top(0,0) will become Right,Top(41,0)</para>
        /// <para>90 ; i_Mirror=1 : Left,Top(0,0) will become Right,Bottom(41,29)</para>
        /// <para>180 ; i_Mirror=0 : Left,Top(0,0) will become Right,Bottom(29.41)</para>
        /// <para>180 ; i_Mirror=1 : Left,Top(0,0) will become Right,Bottom(0.41)</para>
        /// <para>270 ; i_Mirror=0 : Left,Top(0,0) will become Bottom,Left(0,29)</para>
        /// <para>270 ; i_Mirror=1 : Left,Top(0,0) will become Bottom,Right(0,29)</para>
        /// <para>The SetPixel, GetPixel calls are extremly slow on Framework.Compact</para>
        /// <para>Do not use while Drawing, but load and Convert needed Bitmaps at Startup</para>
        /// <para>TODO : 45 - no Idea how do do this</para>
        /// <para>some thoughts : Calculate how many Pixel from (0,0) to (Width,Height)</para>
        /// <para> : Build new Rectangel/Bitmap with new (bigger) size</para>
        /// <para> : Fill Bitmap with transparent Colour</para>
        /// <para> : ImageAttributes.SetColorKey(Color.Magenta,Color.Magenta)</para>
        /// <para> : Draw Bitmap accourding to some Formel</para>
        /// <para>UNKNOWN : Formel to do this</para>
        /// <para>Mark Johnson, Berlin Germany - Click here to reveal e-mail address</para>
        /// </remarks>
        /// </summary>
        /// <param name="i_Degree">Supported ; 0,90,180,270</param>
        /// <param name="i_Mirror">0=No ; 1 = Yes</param>
        /// <param name="bmp">Bitmap to convert</param>
        /// <returns>Converted Bitmap</returns>
        /// <example>
        /// <code>
        /// if (i == i_Work_Back_Rotate)
        /// bmp_Work[i] = Bitmap_Rotate(90,0,bmp_Work[i_Work_Back]);
        /// </code>
        /// </example>
        public Bitmap Bitmap_Rotate(int i_Degree, int i_Mirror, Bitmap bmp)
        {
            Bitmap bmp_New = null;
            if ((i_Degree != 0) && (i_Degree != 90) && (i_Degree != 180) && (i_Degree
            != 270))
                i_Degree = 180; // Assume 180, otherwise no idea what to do
            if ((i_Degree == 90) || (i_Degree == 270))
            {
                bmp_New = new Bitmap(bmp.Height, bmp.Width);
            }
            if ((i_Degree == 0) || (i_Degree == 180))
            {
                bmp_New = new Bitmap(bmp.Width, bmp.Height);
            }
            for (int i = 0; i < bmp.Width; i++) // bmp_Width(29)
            {
                for (int j = 0; j < bmp.Height; j++) // bmp_Height(41)
                {
                    if (i_Degree == 0)
                    {
                        if (i_Mirror == 0) // Left,Top(0,0) will become Left,Top(0,0)
                            bmp_New.SetPixel(i, j, bmp.GetPixel(i, j));
                        else // Left,Top(0,0) will become Right,Top(29,0)
                            bmp_New.SetPixel(((bmp_New.Width - 1) - i), j, bmp.GetPixel(i, j));
                    } // if (i_Degree == 0)
                    if (i_Degree == 90)
                    {
                        if (i_Mirror == 0) // Left,Top(0,0) will become Right,Top(41,0)
                            bmp_New.SetPixel(((bmp_New.Width - 1) - j), i, bmp.GetPixel(i, j));
                        else // Left,Top(0,0) will become Right,Bottom(41,29)

                            bmp_New.SetPixel(((bmp_New.Width - 1) - j), ((bmp_New.Height - 1) - i), bmp.GetPixel(i
                            , j));
                    } // if (i_Degree == 90)
                    if (i_Degree == 180)
                    {
                        if (i_Mirror == 0) // Left,Top(0,0) will become Right,Bottom(29.41)

                            bmp_New.SetPixel(((bmp_New.Width - 1) - i), ((bmp_New.Height - 1) - j), bmp.GetPixel(i
                            , j));
                        else // Left,Top(00) will become Left,Bottom(0,41)
                            bmp_New.SetPixel(i, ((bmp_New.Height - 1) - j), bmp.GetPixel(i, j));
                    } // if (i_Degree == 180) // Left,Top(0,0) will become Right,Bottom(29.41)
                    if (i_Degree == 270)
                    {
                        if (i_Mirror == 0) // Left,Top(0,0) will become Bottom,Left(0,29)
                            bmp_New.SetPixel(j, ((bmp_New.Height - 1) - i), bmp.GetPixel(i, j));
                        else // Left,Top(0,0) will become Bottom,Right(0,29)
                            bmp_New.SetPixel(j, i, bmp.GetPixel(i, j));
                    } // if (i_Degree == 270) // Left,Top(0,0) will become Bottom,Left(0,29)
                } // for (int j=0;j<bmp.Height;j++)
            } // for (int i=0;i<bmp.Width;i++)
            return bmp_New;
        } // public Bitmap Bitmap_Rotate(int i_Degree,Bitmap bmp)
        #endregion

    }

    public enum AnchorPosition
    {
        Top,
        Bottom,
        Left,
        Right,
        Center,
    }

}
